package com.omt.tarjimdinek.security;

import static com.google.common.collect.Lists.newArrayList;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.omt.tarjimdinek.context.UserWithId;
import com.omt.tarjimdinek.domain.user.Service.UtilisateurRepository;
import com.omt.tarjimdinek.domain.user.entity.Role;
import com.omt.tarjimdinek.domain.user.entity.Utilisateur;

/**
 * An implementation of Spring Security's UserDetailsService.
 */
@Named("userDetailsService")
@Service
@Singleton
public class UserDetailsServiceImpl implements UserDetailsService {

    private static final Logger log = Logger.getLogger(UserDetailsServiceImpl.class);

    private UtilisateurRepository accountRepository;

    @Inject
    public UserDetailsServiceImpl(UtilisateurRepository accountRepository) {
        this.accountRepository = accountRepository;
    }

    /**
     * Retrieve an account depending on its login this method is not case sensitive.<br>
     * use <code>obtainAccount</code> to match the login to either email, login or whatever is your login logic
     *
     * @param username the user's login
     * @return a Spring Security userdetails object that matches the login
     * @see #obtainAccount(String)
     * @throws UsernameNotFoundException when the user could not be found
     * @throws DataAccessException when an error occurred while retrieving the account
     */
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
        if (username == null || username.trim().isEmpty()) {
            throw new UsernameNotFoundException("Empty username");
        }

        if (log.isDebugEnabled()) {
            log.debug("Security verification for user '" + username + "'");
        }
 
        Utilisateur account = accountRepository.getUtilisateurByUserName(username);
        
        if (account == null) {
            if (log.isInfoEnabled()) {
                log.info("User " + username + " could not be found");
            }
            throw new UsernameNotFoundException("user " + username + " could not be found");
        }

        Collection<GrantedAuthority> grantedAuthorities = toGrantedAuthorities(account.getListeRoles());
        String password = account.getPassword();
        boolean enabled = account.getEnabled();
        boolean accountNonExpired = true;
        boolean credentialsNonExpired = true;
        boolean accountNonLocked = true;
        Serializable id = account.getId();

        return new UserWithId(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities, id);
    }

    private Collection<GrantedAuthority> toGrantedAuthorities(Collection<Role> collection) {
        List<GrantedAuthority> result = newArrayList();

        for (Role role : collection) {
            result.add(new SimpleGrantedAuthority(role.getLibelle()));
        }

        return result;
    }
}